#include "PrecompiledHeader.h"
#include "CSdl.h"

/*--------------------------------------------------------------------------*/
CSdl* g_Sdl = NULL;
/*--------------------------------------------------------------------------*/

CSdl::CSdl() :
	IEngMod("CSdl")
{
	AssertModule(g_Shell);
	g_Shell->RegisterLuaFunction("getticks", sh::getticks);
	g_Shell->RegisterLuaFunction("getdeltatime", sh::getdeltatime);
	g_Shell->RegisterLuaFunction("getfps", sh::getfps);
	g_Shell->RegisterLuaFunction("getframenumber", sh::getframenumber);

	this->m_currentSecondFpsCount = 0;
	this->m_fpsCounterTimer = 0;
	this->m_frameNumber = 0;

	/*--------------------------------------------------------------------------*/

	int audio_rate = 44100;//22050;
	Uint16 audio_format = AUDIO_S16;
	int audio_channels = 2;
	int audio_buffers = 4096;

	AssertLogC(SDL_Init(SDL_INIT_EVERYTHING) != 0, "Error during SDL_Init: %s", SDL_GetError());
	AssertLogC(TTF_Init() != 0, "Error during TTF_Init: %s", SDL_GetError());
	//AssertLogC(IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG) != 0, "Error during IMG_Init: %s", SDL_GetError());
	AssertLogC(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) != 0, "Error during Mix_OpenAudio: %s", SDL_GetError());

	/*--------------------------------------------------------------------------*/

	this->CreateSdlWindow();
	this->SetWindowTitle("ViaLactea Engine");

	this->InitMutexes();

	/*--------------------------------------------------------------------------*/

	for (uint i = 0; i < CSDL_FPS_HISTORY_SIZE; ++i)
	{
		this->m_fpsHistory[i] = 0;
	}

	this->CountFrame();

	ViaMain::CallOnLoop(this, 0, 50, "CountFps");
	ViaMain::CallOnLoop(this, 1, 60, "SDLEvents");
}

/*--------------------------------------------------------------------------*/

CSdl::~CSdl()
{
	Mix_HaltMusic();
	Mix_CloseAudio();
	SDL_Quit();
}

/*--------------------------------------------------------------------------*/

void CSdl::CreateSdlWindow()
{
	if (g_Settings->GetVSyncEnabled())
	{
		SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
	}

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

	SDL_ShowCursor(0);
	SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);

	SDL_putenv("SDL_VIDEO_CENTERED=center");

	uint flags = SDL_OPENGL | SDL_GL_ACCELERATED_VISUAL | SDL_DOUBLEBUF;

	if (g_Settings->GetFullscreenEnabled())
	{
		flags |= SDL_FULLSCREEN;
	}

	this->m_screenSdl = SDL_SetVideoMode(g_Settings->GetResolutioni().x, g_Settings->GetResolutioni().y, 32, flags);
	AssertLogC(this->m_screenSdl == NULL, "Error during creating SDL_Surface for screen: %s", SDL_GetError());

	int glewError = glewInit();
	AssertLogI(true, "Vendor = %s\nRenderer = %s\nVersion = %s\nGlewInit = %d", glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION), glewError);
}

/*--------------------------------------------------------------------------*/

void CSdl::InitMutexes()
{
	this->m_mutexes = new SDL_mutex*[EMutex::Count];

	for (uint i = 0; i < EMutex::Count; ++i)
	{
		this->m_mutexes[i] = SDL_CreateMutex();
	}
}

/*--------------------------------------------------------------------------*/

void CSdl::CountFrame()
{
	this->m_sdlTicks = SDL_GetTicks();

	if (this->m_fpsCounterTimer + (1000 / CSDL_FPS_HISTORY_SIZE) > this->m_sdlTicks)
	{
		++this->m_currentSecondFpsCount;
	}
	else
	{
		this->m_fpsCounterTimer = this->m_sdlTicks;

		for (uint i = 1; i < CSDL_FPS_HISTORY_SIZE; ++i)
		{
			this->m_fpsHistory[i] = this->m_fpsHistory[i - 1];
		}

		this->m_fpsHistory[0] = this->m_currentSecondFpsCount;

		this->m_averageFps = 0;

		for (uint i = 0; i < CSDL_FPS_HISTORY_SIZE; ++i)
		{
			this->m_averageFps += this->m_fpsHistory[i];
		}

		this->m_currentSecondFpsCount = 0;
	}

	++this->m_frameNumber;
}

/*--------------------------------------------------------------------------*/

void CSdl::ProcessEvents()
{
	this->m_sdlEventsList.clear();

	SDL_Event event;

	while (SDL_PollEvent(&event))
	{
		if (event.type == SDL_QUIT)
		{
			ViaMain::StopGame();
		}

		this->m_sdlEventsList.push_back(event);
	}
}

/*--------------------------------------------------------------------------*/

void CSdl::OnLoop(const uint i)
{
	switch (i)
	{
	case 0:
	{
		this->CountFrame();

		std::stringstream ss;
		ss << "FPS = " << this->m_averageFps;
		g_Hud->GetStatisticsManager()->SetLabel("CSdl", ss.str());

		this->m_deltaTime = this->m_timerForDeltaTime.GetTimer() / 1000.0f;
		this->m_timerForDeltaTime.SetTimer();

		//this->BenchmarkLog("CSdl::CountFrame");

		break;
	}

	case 1:
	{
		this->ProcessEvents();
		//this->BenchmarkLog("CSdl::ProcessEvents");
		break;
	}
	}
}

/*--------------------------------------------------------------------------*/

uint CSdl::GetTicks() const
{
	return this->m_sdlTicks;
}

/*--------------------------------------------------------------------------*/

uint CSdl::GetAverageFps() const
{
	return this->m_averageFps;
}

/*--------------------------------------------------------------------------*/

std::list<SDL_Event>& CSdl::GetEvents()
{
	return this->m_sdlEventsList;
}

/*--------------------------------------------------------------------------*/

void CSdl::SetWindowTitle(const std::string& title)
{
	this->m_windowTitle = title;
	SDL_WM_SetCaption(this->m_windowTitle.c_str(), NULL);
}

/*--------------------------------------------------------------------------*/

const std::string& CSdl::GetWindowTitle() const
{
	return this->m_windowTitle;
}

/*--------------------------------------------------------------------------*/

uint CSdl::GetFrameNumber() const
{
	return this->m_frameNumber;
}

/*--------------------------------------------------------------------------*/

float CSdl::GetDeltaTime() const
{
	return this->m_deltaTime;
}

/*--------------------------------------------------------------------------*/

uint CSdl::GetCurrentSecondFps() const
{
	return this->m_currentSecondFpsCount;
}

/*--------------------------------------------------------------------------*/

void CSdl::SwapBuffers()
{
	SDL_GL_SwapBuffers();
}

/*--------------------------------------------------------------------------*/

void CSdl::Delay(uint ms)
{
	SDL_Delay(ms);
}

/*--------------------------------------------------------------------------*/

int sh::getticks()
{
	AssertModule(g_Sdl);
	return g_Sdl->GetTicks();
}

/*--------------------------------------------------------------------------*/

float sh::getdeltatime()
{
	AssertModule(g_Sdl);
	return g_Sdl->GetDeltaTime();
}

/*--------------------------------------------------------------------------*/

uint sh::getframenumber()
{
	AssertModule(g_Sdl);
	return g_Sdl->GetFrameNumber();
}

/*--------------------------------------------------------------------------*/

uint sh::getfps()
{
	AssertModule(g_Sdl);
	return g_Sdl->GetAverageFps();
}

/*--------------------------------------------------------------------------*/